#ifndef __BSTREE_REGISTER_ClASSES_H__
#define __BSTREE_REGISTER_ClASSES_H__


#include <iostream>
#include <string>
#include "./includes/file_pointer_pack.h"
#include "./includes/register_pack.h"

namespace invertedindex{

class KeyKeyVal : public Register{
	private:
		static const unsigned int KEY_SIZE = 20;
	private:
		char* key;
	public:
		KeyKeyVal(unsigned int keyLength, char* key){
			this->key=new char[KEY_SIZE];
			memset(this->key,0,KEY_SIZE);
			if(keyLength<KEY_SIZE)
				memcpy(this->key, key, keyLength);
			else
				memcpy(this->key, key, KEY_SIZE);
		}

		KeyKeyVal(const KeyKeyVal& k2){
			this->key=k2.getKey();
		}

		~KeyKeyVal(){
			delete[] key;
		}
		
		char* getKey()const{
			char* ret = new char[KEY_SIZE];
			memcpy(ret,this->key, KEY_SIZE);
			return ret;
		}

		Register* duplicate()const{
			return (new KeyKeyVal(*this));
		}

		void setFields(const Register& b2){
			delete[] this->key;
			const KeyKeyVal& temp = dynamic_cast<const KeyKeyVal&>(b2);
			this->key = temp.getKey();
		}

		unsigned int getSize()const {
			return KEY_SIZE;
		};

		bool operator<(const Register& b2)const{
			const KeyKeyVal& temp = dynamic_cast<const KeyKeyVal&>(b2);

			bool ret = false;
			char* w1=this->getKey();
			char* w2=temp.getKey();
			int cmp=memcmp ( w1, w2, KEY_SIZE);
			if(cmp<0)
				ret=true;

			delete[] w1;
			delete[] w2;

			return ret;
		}

		std::ostream& toOstream(std::ostream& out)const{
			out << this->key;
			return out;
		}
};


class KeyVal : public Register{

	private:
		unsigned int keyLength;
		char* key;
		unsigned int idKey;
		BlockPointer* bp;
		unsigned int wordCount;
		unsigned int lexicoLength;
		char* lexico;
	public:
		KeyVal(unsigned int keyLength, const char* key, unsigned int idKey){
			this->key=new char[keyLength];
			memcpy(this->key,key,keyLength);
			this->keyLength=keyLength;
			this->idKey = idKey;
			this->bp=NULL;
			this->wordCount=0;
			this->lexicoLength=0;
			lexico=NULL;
		}
		KeyVal(const KeyVal& k2){
			std::pair<unsigned int, char*> keyAux=k2.getKey();
			this->key = keyAux.second;
			this->keyLength = keyAux.first;
			this->idKey = k2.getIdKey();
			this->bp = k2.getBP();
			this->wordCount=k2.getWordCount();
			std::pair<unsigned int, char*> lex=k2.getLexico();
			this->lexicoLength=lex.first;
			this->lexico=lex.second;
		}

		~KeyVal(){
			delete[] key;
			delete[] lexico;
			delete bp;
		}

		std::pair<unsigned int, char*> getKey()const{ 
			std::pair<unsigned int, char*> ret(this->keyLength,NULL);
			ret.second = new char[this->keyLength];
			memcpy(ret.second, this->key, this->keyLength);
			return ret;  
		}
		unsigned int getIdKey()const{ return this->idKey; }
		BlockPointer* getBP()const{ 
			if(bp!=NULL)
				return new BlockPointer(*bp); 
			
			return NULL;
		}
		unsigned int getWordCount()const{ return this->wordCount; }
		std::pair<unsigned int, char*> getLexico()const{
			std::pair<unsigned int, char*> ret(this->lexicoLength,NULL);
			if(this->lexicoLength>0){
				ret.second = new char[this->lexicoLength];
				memcpy(ret.second, this->lexico, this->lexicoLength);
			}
			return ret;
		}
		
		void setKey(unsigned int keyLength, char* key){
			this->keyLength = keyLength;
			delete[] this->key;
			this->key = new char[keyLength];
			memcpy(this->key, key, keyLength);
			
		}
		void setIdKey(unsigned int idKey){ this->idKey=idKey; }
		void setBP(const BlockPointer* bp){ 
			delete this->bp;
			if(bp!=NULL)
				this->bp=new BlockPointer(*bp); 
			else
				this->bp=NULL;
		}
		void setWordCount(unsigned int wordCount){ this->wordCount=wordCount; }
		void setLexico(unsigned int lexicoLength, const char* lexico){ 
			this->lexico=new char[lexicoLength];
			memcpy(this->lexico,lexico, lexicoLength);
			this->lexicoLength=lexicoLength;
		}
		
		Register* duplicate()const{
			return (new KeyVal(*this));
		}

		void setFields(const Register& b2){
			const KeyVal& temp = dynamic_cast<const KeyVal&>(b2);
			std::pair<unsigned int, char*> keyAux=temp.getKey();
			delete[] this->key;
			this->keyLength=keyAux.first;
			this->key=keyAux.second;
			this->idKey = temp.getIdKey();
			this->bp = temp.getBP();
			this->wordCount=temp.getWordCount();
			std::pair<unsigned int, char*> lex=temp.getLexico();
			this->lexicoLength=lex.first;
			this->lexico=lex.second;
		}

		unsigned int getSize()const {
			unsigned int ret=0;
			if(bp==NULL){
				BlockPointer bpa(0,0);
				ret=sizeof(unsigned int)*4+keyLength+lexicoLength+bpa.getSize();
			}
			else
				ret=sizeof(unsigned int)*4+keyLength+lexicoLength+bp->getSize();
			return ret;
		};

		bool operator<(const Register& b2)const{
			const KeyVal& temp = dynamic_cast<const KeyVal&>(b2);
			bool ret = false;
			std::pair<unsigned int, char*> w1=this->getKey();
			std::pair<unsigned int, char*> w2=temp.getKey();

			int cmp=0;
			if(w1.first<w2.first)
				cmp=memcmp ( w1.second, w2.second, w1.first);
			else
				cmp=memcmp ( w1.second, w2.second, w2.first);
			if((cmp==0)&&((w2.first)>(w1.first))) {ret=true;}
			if(cmp<0)
				ret=true;
			
			delete[] w1.second;
			delete[] w2.second;
			return ret;
		}

		Register* getKeyRegister()const{
			KeyKeyVal* reg = new KeyKeyVal(this->keyLength, this->key);
			return reg;
		}

		std::ostream& toOstream(std::ostream& out)const{
			char* temp=key;
			unsigned int i;
			out << "\t\t";
			for(i=0; i<keyLength; i++){
				out<<*temp;
				temp++;
			}
			out << std::endl;
			out << "\t\t KeyLength   : " << this->keyLength << std::endl;
			out << "\t\t IdKey : " << this->idKey << std::endl;
			if(bp!=NULL)
				bp->toOstream(out);
			else
				out<<"\t\t no File Pointer"<<std::endl;
			out << "\t\t wordCount: " << this->wordCount << std::endl;
			out << "\t\t LexicoLength : " << this->lexicoLength << std::endl;
			return out;
		}
};


class KeyKeyValFactory : public RegisterFactory{
	private:
		static const unsigned int KEY_SIZE = 20;
	public:
		KeyKeyValFactory(){}
		~KeyKeyValFactory(){}

		RegisterFactory* duplicate()const{ return new KeyKeyValFactory(); }

		Register* operator ()(char* data)const{
			return (new KeyKeyVal(KEY_SIZE,data));
		}
		
		char* operator ()(const Register& reg,char* data)const{
			const KeyKeyVal& kkvreg = dynamic_cast<const KeyKeyVal&>(reg);
			char* temp = data;
			char* tempKey = kkvreg.getKey();
			memcpy(temp,tempKey,KEY_SIZE);
			temp+=KEY_SIZE;
			delete[] tempKey;
			return temp;
		}
};

class KeyValFactory : public RegisterFactory{
	private:
		static const unsigned int NOFILEPOINTER=-1;
	public:
		KeyValFactory(){}
		~KeyValFactory(){}

		RegisterFactory* duplicate()const{ return new KeyValFactory(); }

		Register* operator ()(char* data)const{
			char* temp = data;
			unsigned int IdKey;
			unsigned int keyLength;
			unsigned int wordCount;
			unsigned int lexicoLength;
			char* keyTemp;
			char* lexicoTemp;

			memcpy(&keyLength,temp,sizeof(unsigned int));
			temp += sizeof(unsigned int);
			
			keyTemp = new char[keyLength];
			memcpy(keyTemp,temp,keyLength);
			temp += keyLength;

			memcpy(&IdKey,temp,sizeof(unsigned int));
			temp += sizeof(unsigned int);

			BlockPointer blockPointer(0,0);
			temp=blockPointer.toFilePointer(temp);

			memcpy(&wordCount,temp,sizeof(unsigned int));
			temp += sizeof(unsigned int);

			memcpy(&lexicoLength,temp,sizeof(unsigned int));
			temp += sizeof(unsigned int);
			
			lexicoTemp = new char[lexicoLength];
			memcpy(lexicoTemp,temp,lexicoLength);
			temp += lexicoLength;

			KeyVal* kv = new KeyVal(keyLength,keyTemp,IdKey);
			if(blockPointer.getBlock()!=NOFILEPOINTER)
				kv->setBP(&blockPointer);
			else
				kv->setBP(NULL);
			kv->setLexico(lexicoLength,lexicoTemp);
			kv->setWordCount(wordCount);

			delete[] keyTemp;
			delete[] lexicoTemp;
			return (kv);
		}
		
		char* operator ()(const Register& reg,char* data)const{
			const KeyVal& kvreg = dynamic_cast<const KeyVal&>(reg);
			char* temp = data;
			unsigned int tempIdKey = kvreg.getIdKey();
			unsigned int wordCount = kvreg.getWordCount();
			std::pair<unsigned int, char*> tempKey = kvreg.getKey();
			unsigned int keyLength = tempKey.first;
			std::pair<unsigned int, char*> tempLexico = kvreg.getLexico();
			unsigned int lexicoLength = tempLexico.first;

			memcpy(temp,&keyLength,sizeof(unsigned int));
			temp+=sizeof(unsigned int);
			
			memcpy(temp,tempKey.second,tempKey.first);
			temp+=tempKey.first;
			delete[] tempKey.second;

			memcpy(temp,&tempIdKey,sizeof(unsigned int));
			temp+=sizeof(unsigned int);

			BlockPointer* blocPoint=kvreg.getBP();
			if(blocPoint==NULL)
				blocPoint=new BlockPointer(NOFILEPOINTER,NOFILEPOINTER);
			temp=blocPoint->toBytes(temp);
			delete blocPoint;

			memcpy(temp,&wordCount,sizeof(unsigned int));
			temp+=sizeof(unsigned int);

			memcpy(temp,&lexicoLength,sizeof(unsigned int));
			temp+=sizeof(unsigned int);

			memcpy(temp,tempLexico.second,tempLexico.first);
			temp+=lexicoLength;
			delete[] tempLexico.second;
			
			return temp;
		}
};

}

#endif
